﻿using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Math.EC;
using Org.BouncyCastle.Utilities.Encoders;
using RuoVea.SM.SMExtend;
using System;
using System.Text;
using CryptographicException = System.Security.Cryptography.CryptographicException;

namespace RuoVea.SM;

/// <summary>
/// SM2国密算法工具类（基于BouncyCastle实现）
/// 提供SM2加密、解密、密钥生成等功能
/// </summary>
public static class SM2Crypt
{
    // 默认密钥（仅用于演示，生产环境应使用GenerateKeyPair生成的密钥）
    private const string DefaultPublicKey = "04F6E0C3345AE42B51E06BF50B98834988D54EBC7460FE135A48171BC0629EAE205EEDE253A530608178A98F1E19BB737302813BA39ED3FA3C51639D7A20C7391A";
    private const string DefaultPrivateKey = "3690655E33D5EA3D9A4AE1A1ADD766FDEA045CDEAA43A9206FB8C430CEFE0D94";

    // 密钥长度常量（16进制字符长度）
    private const int ExpectedPublicKeyLength = 130;  // 非压缩格式公钥长度
    private const int ExpectedPrivateKeyLength = 64;  // 私钥长度
    private const int C1Length = 130;                // C1分量长度
    private const int C3Length = 64;                 // C3分量长度

    /// <summary>
    /// 使用指定公钥加密文本
    /// </summary>
    /// <param name="publicKeyHex">16进制格式的公钥</param>
    /// <param name="plainText">要加密的文本</param>
    /// <returns>加密后的16进制字符串</returns>
    /// <exception cref="ArgumentNullException">参数为空</exception>
    /// <exception cref="ArgumentException">无效的公钥</exception>
    /// <exception cref="CryptographicException">加密失败</exception>
    public static string Encrypt(string publicKeyHex, string plainText)
    {
        if (string.IsNullOrEmpty(publicKeyHex))
            throw new ArgumentNullException(nameof(publicKeyHex));
        if (string.IsNullOrEmpty(plainText))
            throw new ArgumentNullException(nameof(plainText));
        if (publicKeyHex.Length != ExpectedPublicKeyLength)
            throw new ArgumentException($"Public key must be {ExpectedPublicKeyLength} characters long", nameof(publicKeyHex));

        try
        {
            var publicKeyBytes = Hex.Decode(publicKeyHex);
            var plainData = Encoding.UTF8.GetBytes(plainText);
            return Encrypt(publicKeyBytes, plainData);
        }
        catch (Exception ex)
        {
            throw new CryptographicException("SM2 encryption failed", ex);
        }
    }

    /// <summary>
    /// 使用默认公钥加密文本
    /// </summary>
    public static string EncryptWithDefaultKey(string plainText)
    {
        return Encrypt(DefaultPublicKey, plainText);
    }

    /// <summary>
    /// 使用指定私钥解密文本
    /// </summary>
    /// <param name="privateKeyHex">16进制格式的私钥</param>
    /// <param name="cipherTextHex">要解密的16进制密文</param>
    /// <returns>解密后的原始文本</returns>
    /// <exception cref="ArgumentNullException">参数为空</exception>
    /// <exception cref="ArgumentException">无效的私钥或密文</exception>
    /// <exception cref="CryptographicException">解密失败</exception>
    public static string Decrypt(string privateKeyHex, string cipherTextHex)
    {
        if (string.IsNullOrEmpty(privateKeyHex))
            throw new ArgumentNullException(nameof(privateKeyHex));
        if (string.IsNullOrEmpty(cipherTextHex))
            throw new ArgumentNullException(nameof(cipherTextHex));
        if (privateKeyHex.Length != ExpectedPrivateKeyLength)
            throw new ArgumentException($"Private key must be {ExpectedPrivateKeyLength} characters long", nameof(privateKeyHex));
        if (cipherTextHex.Length <= C1Length + C3Length)
            throw new ArgumentException("Invalid cipher text length", nameof(cipherTextHex));

        try
        {
            var privateKeyBytes = Hex.Decode(privateKeyHex);
            var encryptedData = Hex.Decode(cipherTextHex);
            var decryptedData = Decrypt(privateKeyBytes, encryptedData);
            return Encoding.UTF8.GetString(decryptedData);
        }
        catch (Exception ex)
        {
            throw new CryptographicException("SM2 decryption failed", ex);
        }
    }

    /// <summary>
    /// 使用默认私钥解密文本
    /// </summary>
    public static string DecryptWithDefaultKey(string cipherTextHex)
    {
        return Decrypt(DefaultPrivateKey, cipherTextHex);
    }

    /// <summary>
    /// 生成SM2密钥对
    /// </summary>
    /// <returns>元组(公钥, 私钥)，均为16进制字符串</returns>
    /// <exception cref="CryptographicException">密钥生成失败</exception>
    public static (string PublicKey, string PrivateKey) GenerateKeyPair()
    {
        try
        {
            SM2 sm2 = SM2.Instance;
            AsymmetricCipherKeyPair key = sm2.ecc_key_pair_generator.GenerateKeyPair();
            ECPrivateKeyParameters ecpriv = (ECPrivateKeyParameters)key.Private;
            ECPublicKeyParameters ecpub = (ECPublicKeyParameters)key.Public;

            // 使用非压缩格式编码公钥
            string publicKeyHex = Hex.ToHexString(ecpub.Q.GetEncoded(false)).ToUpper();
            // 使用无符号大整数格式编码私钥
            string privateKeyHex = Hex.ToHexString(ecpriv.D.ToByteArrayUnsigned()).ToUpper();

            return (publicKeyHex, privateKeyHex);
        }
        catch (Exception ex)
        {
            throw new CryptographicException("Failed to generate SM2 key pair", ex);
        }
    }

    #region internal
    /// <summary>
    /// 使用字节数组公钥加密数据
    /// </summary>
    /// <param name="publicKey">公钥字节数组</param>
    /// <param name="plainData">要加密的原始数据</param>
    /// <returns>加密后的16进制字符串</returns>
    internal static string Encrypt(byte[] publicKey, byte[] plainData)
    {
        if (publicKey == null || publicKey.Length == 0)
            throw new ArgumentNullException(nameof(publicKey));
        if (plainData == null || plainData.Length == 0)
            throw new ArgumentNullException(nameof(plainData));

        try
        {
            SM2 sm2 = SM2.Instance;
            var cipher = new Cipher();
            ECPoint userKey = sm2.ecc_curve.DecodePoint(publicKey);

            // 初始化加密并获取C1分量
            ECPoint c1 = cipher.Init_enc(sm2, userKey);

            // 加密数据（C2分量）
            byte[] c2 = new byte[plainData.Length];
            Array.Copy(plainData, c2, plainData.Length);
            cipher.Encrypt(c2);

            // 计算哈希（C3分量）
            byte[] c3 = new byte[32];
            cipher.Dofinal(c3);

            // 拼接C1||C2||C3
            return Hex.ToHexString(c1.GetEncoded(false)) + Hex.ToHexString(c2) + Hex.ToHexString(c3);
        }
        catch (Exception ex)
        {
            throw new CryptographicException("SM2 encryption failed", ex);
        }
    }

    /// <summary>
    /// 使用字节数组私钥解密数据
    /// </summary>
    /// <param name="privateKey">私钥字节数组</param>
    /// <param name="encryptedData">加密后的字节数组</param>
    /// <returns>解密后的原始数据</returns>
    internal static byte[] Decrypt(byte[] privateKey, byte[] encryptedData)
    {
        if (privateKey == null || privateKey.Length == 0)
            throw new ArgumentNullException(nameof(privateKey));
        if (encryptedData == null || encryptedData.Length == 0)
            throw new ArgumentNullException(nameof(encryptedData));

        try
        {
            string dataHex = Hex.ToHexString(encryptedData);

            // 解析C1分量（前130个16进制字符）
            byte[] c1Bytes = Hex.Decode(dataHex.Substring(0, C1Length));

            // 计算C2分量长度
            int c2Length = (dataHex.Length - C1Length - C3Length) / 2;
            byte[] c2 = Hex.Decode(dataHex.Substring(C1Length, c2Length * 2));

            // 解析C3分量（最后64个16进制字符）
            byte[] c3 = Hex.Decode(dataHex.Substring(C1Length + c2Length * 2, C3Length));

            SM2 sm2 = SM2.Instance;
            var userD = new BigInteger(1, privateKey);
            ECPoint c1 = sm2.ecc_curve.DecodePoint(c1Bytes);

            var cipher = new Cipher();
            cipher.Init_dec(userD, c1);
            cipher.Decrypt(c2);
            cipher.Dofinal(c3);

            return c2;
        }
        catch (Exception ex)
        {
            throw new CryptographicException("SM2 decryption failed", ex);
        }
    } 
    #endregion
}